package mbp.recorder;

import java.io.File;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import mbp.agents.MbpManagerAgent;
import mbp.core.Resource;
import mbp.core.ResourceUtility;
import mbp.core.ResourceValidation;
import mbp.core.Service;
import mbp.message.ObjectBuilder;
import mbp.message.TagBuilder;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * Implementuje funkcjonalnosc nagrywania, zapisywania uruchamianych agentow oraz
 * wszystkich wyslanych do agenta obiektow. Klasa przeznaczona do testowania systemu
 * oraz uruchaminaia agentow gotowych do pracy.
 * @author Wojciech Wilanski
 *
 */
public class AgentRecorder 
{
	//private MbpManagerGui mbpManagerGui;
	private TagBuilder tagBuilder;
	private ObjectBuilder objectBuilder;
	
	//Dokument przeznaczony do zapisywania agentow
	private Document agentsDoc;
	private Element rootElement;
	
	Logger logger = Logger.getLogger(this.getClass());
	
	public AgentRecorder()
	{
		//this.mbpManagerGui = mbpManagerGui;
		tagBuilder = new TagBuilder();
		objectBuilder = new ObjectBuilder();
		agentsDoc = null;
		rootElement = null;
	}
	
	/**
	 * Ustawia obiekt w stan nagrywania. Nagrywanie odbywa sie w pamieci komputera.
	 */
	public void startRecording()
	{
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db;
		try {
			db = dbf.newDocumentBuilder();
			agentsDoc = db.newDocument();
			rootElement = agentsDoc.createElement("agents");
			agentsDoc.appendChild(rootElement);
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * Zatrzymuje nagrywanie i zapisuje szystko co zostalo zgromadzone do pliku.
	 * @param fileName Sciezka z nazwa pliku
	 */
	public void stopRecording(String fileName)
	{
		if(agentsDoc == null) return;
		
		TransformerFactory transformerFactory = TransformerFactory.newInstance();
		try {
			Transformer transformer = transformerFactory.newTransformer();
			DOMSource source = new DOMSource(agentsDoc);
			StreamResult result = new StreamResult(new File(fileName));
			transformer.transform(source, result);
			logger.info("File saved!");
			agentsDoc = null;
			rootElement = null;
		} catch (TransformerConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (TransformerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * Rozpoczyna nagrywanie z opcja kontynuacji poprzedniego nagrywania. Do pamieci
	 * zostajwe zaladowany plik z poprzedniego nagrywanaia i nastepuje kontynuacja.
	 * @param fileName Sciezka do pliku z wynikiem poprzedniego nagrywanaia
	 */
	public void continueRecording(String fileName)
	{
		File fileAgents = new File(fileName);
		DocumentBuilder db;
		
		try {
			db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			agentsDoc = db.parse(fileAgents);
			rootElement = (Element) agentsDoc.getElementsByTagName("agents").item(0);		
			
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			logger.error("ParserConfigurationException", e);
			return;
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			logger.error("SAXException", e);
			return;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			logger.error("IOException", e);
			return;
		}
	}
	
	/**
	 * Zapamietuje uruchomienie nowego agenta
	 * @param agentClass Klasa egenta
	 * @param agentName Nazwa agenta
	 */
	public void startNewAgent(String agentClass, String agentName)
	{
		if(agentsDoc == null) return;
		
		Element agentElement = agentsDoc.createElement("agent");
		agentElement.setAttribute("class", agentClass);
		agentElement.setAttribute("name", agentName);
		rootElement.appendChild(agentElement);
		
	}
	
	/**
	 * Zapamietanie wyslania obiektu Service do agenta
	 * @param agentName Nazwa agenta
	 * @param service Referencja do wyslanego obiektu Service
	 */
	public void sendServiceToAgent(String agentName, Service service)
	{
		if(agentsDoc == null) return;
		
		NodeList agents = rootElement.getElementsByTagName("agent");
		for(int i = 0; i<agents.getLength(); i++)
		{
			Element agentElement = (Element) agents.item(i);
			String name = agentElement.getAttribute("name");
			if(name.compareTo(agentName)==0)
			{
				Element serviceElement = tagBuilder.getServiceTag(agentsDoc, 
						objectBuilder.getServiceInfo(service), "aservice");
				agentElement.appendChild(serviceElement);
			}
		}
	}
	
	/**
	 * Zapamietanie wyslania obiektu Resource do agenta
	 * @param agentName Nazwa agenta
	 * @param resource Referencja do wyslanego obiektu Resource do agenta
	 */
	public void sendResourceToAgent(String agentName, Resource resource)
	{
		if(agentsDoc == null) return;
		
		NodeList agents = rootElement.getElementsByTagName("agent");
		for(int i = 0; i<agents.getLength(); i++)
		{
			Element agentElement = (Element) agents.item(i);
			String name = agentElement.getAttribute("name");
			if(name.compareTo(agentName)==0)
			{
				Element resourceElement = tagBuilder.getResourceTag(agentsDoc, 
						objectBuilder.getResourceInfo(resource), "aresource");
				agentElement.appendChild(resourceElement);
						
						
			}
		}
	}
	
	public void sendUtilityToAgent(String agentName, ResourceUtility resourceUtility)
	{
		if(agentsDoc == null) return;
		
		NodeList agents = rootElement.getElementsByTagName("agent");
		for(int i = 0; i<agents.getLength(); i++)
		{
			Element agentElement = (Element) agents.item(i);
			String name = agentElement.getAttribute("name");
			if(name.compareTo(agentName)==0)
			{
				Element utilityElement = tagBuilder.getResourceUtilityTag(agentsDoc, 
						objectBuilder.getUtilityInfo(resourceUtility), "autility");
				agentElement.appendChild(utilityElement);
				//Element serviceElement = tagBuilder.getServiceTag(agentsDoc, 
				//		objectBuilder.getServiceInfo(service), "service");
				//agentElement.appendChild(serviceElement);
			}
		}
	}
	
	public void sendValidationToAgent(String agentName, ResourceValidation resourceValidation)
	{
		if(agentsDoc == null) return;
		
		NodeList agents = rootElement.getElementsByTagName("agent");
		for(int i = 0; i<agents.getLength(); i++)
		{
			Element agentElement = (Element) agents.item(i);
			String name = agentElement.getAttribute("name");
			if(name.compareTo(agentName)==0)
			{
				Element validationElement = tagBuilder.getResourceValidationTag(agentsDoc, 
						objectBuilder.getValidationInfo(resourceValidation), "avalidation");
				agentElement.appendChild(validationElement);
				//Element serviceElement = tagBuilder.getServiceTag(agentsDoc, 
				//		objectBuilder.getServiceInfo(service), "service");
				//agentElement.appendChild(serviceElement);
			}
		}
	}
	
	/**
	 * Odtwarza agentow i wszystkie wyslane do nich obiekty z pliku
	 * @param mbpManager Referencja do managera
	 * @param fileName Sciezka do pliku
	 */
	public void restoreAgents(MbpManagerAgent mbpManager, String fileName)
	{
		File fileAgents = new File(fileName);
		DocumentBuilder db;
		Document docAgents;
		try {
			db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			docAgents = db.parse(fileAgents);
			
			
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			logger.error("ParserConfigurationException", e);
			return;
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			logger.error("SAXException", e);
			return;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			logger.error("IOException", e);
			return;
		}
		
		NodeList agents = docAgents.getElementsByTagName("agent");
		
		for(int i = 0; i<agents.getLength(); i++)
		{
			Element agentElement = (Element) agents.item(i);
			
			String agentClass = agentElement.getAttribute("class");
			String agentName = agentElement.getAttribute("name");
			
			mbpManager.StartAgent(agentClass, agentName, null);
			
			NodeList services = agentElement.getElementsByTagName("aservice");
			NodeList resources = agentElement.getElementsByTagName("aresource");
			NodeList utilities = agentElement.getElementsByTagName("autility");
			NodeList validations = agentElement.getElementsByTagName("avalidation");
					
			for(int j = 0; j<services.getLength(); j++)
			{
				Element serviceElement = (Element) services.item(j);
				Service srv = objectBuilder.getService(tagBuilder.getServiceInfo(serviceElement));
				
				mbpManager.SendService(srv, agentName);
			}
			
			for(int j = 0; j<resources.getLength(); j++)
			{
				Element resourceElement = (Element) resources.item(j);
				Resource rc = objectBuilder.getResource(tagBuilder.getResourceInfo(resourceElement));
				
				mbpManager.SendResource(rc, agentName);
			}
			
			for(int j = 0; j<utilities.getLength(); j++)
			{
				Element utilityElement = (Element) utilities.item(j);
				ResourceUtility ru = objectBuilder.getResourceUtility(tagBuilder.getUtilityInfo(utilityElement));
				
				mbpManager.SendResourceUtility(ru, agentName);
			}
			
			for(int j = 0; j<validations.getLength(); j++)
			{
				Element validationElement = (Element) validations.item(j);
				ResourceValidation rv = objectBuilder.getResourceValidation(tagBuilder.getValidationInfo(validationElement));
				
				mbpManager.SendResourceValidation(rv, agentName);
			}
			
			
		}
	}

}
